package com.cvte.boe.media.player;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

import com.cvte.boe.media.R;
import com.cvte.boe.media.controller.MediaFileManager;
import com.cvte.boe.media.dao.MediaSettingDao;
import com.cvte.boe.media.dao.MediaSettingDaoImpl;
import com.cvte.boe.media.model.LocalMediaInfo;
import com.cvte.boe.media.player.retrieve.AsyncRetrieveAudio;
import com.cvte.boe.media.player.retrieve.RetrieveAudioCompleteListener;
import com.cvte.boe.media.utils.LogUtil;
import com.cvte.boe.media.utils.LrcUtil;
import com.cvte.boe.media.utils.SettingUtil;
import com.cvte.boe.media.utils.StringUtils;
import com.cvte.boe.media.utils.constant.Constant;
import com.cvte.boe.media.views.GlobalDialog;
import com.cvte.boe.media.views.settingpage.PageAudioPlayerFileList;
import com.cvte.boe.media.views.widget.LongClickImageButton;
import com.cvte.boe.media.views.widget.LrcView;
import com.cvte.boe.media.views.widget.ReflectionImage;
import com.cvte.boe.media.views.widget.MarqueeTextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.crypto.spec.DESedeKeySpec;

/**
 * Created by wuguitong on 2018/4/19.
 */
public class AudioPlayer implements RetrieveAudioCompleteListener,MediaPlayer.OnPreparedListener,MediaPlayer.OnCompletionListener,MediaPlayer.OnErrorListener,
        MediaPlayer.OnInfoListener,MediaPlayer.OnSeekCompleteListener,SeekBar.OnSeekBarChangeListener,PageAudioPlayerFileList.OnAudioPageChangeListener{
    private static final String TAG = AudioPlayer.class.getSimpleName();
    private static final int MIN_BACKWARD_FORWARD_TIME = 1000;
    private static final int SEEKBAR_STEP_COUNT = 25;
    private static final int TIME_UPDATE_AUDIO_INFO = 5 * 100;
    private static final int TIME_UPDATE_PAUSE_VIEW = 5 * 100;
    private static final int TIME_UPDATE_LRC_VIEW = 1 * 100;
    private static final int MSG_UPDATE_ALBUM = 0;
    private static final int MSG_UPDATE_AUDIO_INFO = 1;
    private static final int MSG_UPDATE_PAUSE_VIEW = 2;
    private static final int MSG_UPDATE_LRC_VIEW = 3;
    private static final int MSG_AUDIO_NOT_SUPPORT = 4;
    private Context mContext;
    private String mCurrentFileName;
    private String mCurDir;
    private LongClickImageButton ibtnPauseStart;
    private LongClickImageButton ibtnPlayMode;
    private SeekBar mMusicSeekBar;
    private TextView mCurTime;
    private TextView mDurTime;
    private MarqueeTextView mtvFileName;
    private List<LocalMediaInfo> mLocalMediaInfoList;
    private int mCurrentFileIndex;
    private ReflectionImage mAlbumReflectionImage;
    private AsyncRetrieveAudio mAsyncRetrieveAudio = null;
    private Bitmap mCurBitmap = null;
    private MediaPlayer mPlayer = null;
    private AudioStatus mAudioStatus = AudioStatus.UNKNOW;
    private SettingUtil.MPLAYMODE mCurPlayMode = SettingUtil.MPLAYMODE.PLAYONCE;
    private PageAudioPlayerFileList mPageAudioPlayerFileList;
    private OnAudioPlayerListener mOnAudioPlayerListener;
    private MediaSettingDao dao;
    private LrcView mLrcView;
    private AlertDialog.Builder mAlertDialog = null;

    public void setOnAudioPlayerListener(OnAudioPlayerListener onAudioPlayerListener) {
        this.mOnAudioPlayerListener = onAudioPlayerListener;
    }

    public interface OnAudioPlayerListener{
        public void OnAudioDestroy();
        public void OnAudioUnsupport();
    }
    private enum AudioStatus{
        UNKNOW,PREPAREING,INITED,PLAYING,PAUSE,SEEKING,STOPPED,ERROR
    }
    private Handler audioHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_UPDATE_ALBUM:
                    if(mCurBitmap != null) {
                        mAlbumReflectionImage.setImageBitmap(mCurBitmap);
                    }else {
                        mAlbumReflectionImage.setImageResource(R.drawable.def_album_audio_image);
                    }
                    break;
                case MSG_UPDATE_AUDIO_INFO:
                    LogUtil.d(TAG,mAudioStatus.toString());
                    if(mAudioStatus.ordinal() >= AudioStatus.INITED.ordinal() && mAudioStatus.ordinal() != AudioStatus.SEEKING.ordinal()) {
                        int cur = mPlayer.getCurrentPosition();
                        int dur = mPlayer.getDuration();
                        LogUtil.d(TAG,"MSG_UPDATE_AUDIO_INFO cur = " + cur + " dur = " + dur);
                        mCurTime.setText(StringUtils.formatMediaTimeSECAndMINToString(cur));
                        mDurTime.setText(StringUtils.formatMediaTimeSECAndMINToString(dur));
                        mMusicSeekBar.setProgress(cur);

                        if (mAudioStatus == AudioStatus.PLAYING) {
                            audioHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_INFO, TIME_UPDATE_AUDIO_INFO);
                        }
                    }
                    break;
                case MSG_UPDATE_PAUSE_VIEW:
                    if (mAudioStatus == AudioStatus.PAUSE) {
                        if(mCurTime.getVisibility() == View.VISIBLE){
                            mCurTime.setVisibility(View.GONE);
                        }else{
                            mCurTime.setVisibility(View.VISIBLE);
                        }
                    }else {
                        if(mCurTime.getVisibility() != View.VISIBLE){
                            mCurTime.setVisibility(View.VISIBLE);
                        }
                    }
                    audioHandler.sendEmptyMessageDelayed(MSG_UPDATE_PAUSE_VIEW,TIME_UPDATE_PAUSE_VIEW);
                    break;
                case MSG_UPDATE_LRC_VIEW:
                    if(mAudioStatus == AudioStatus.PLAYING) {
                        mLrcView.seekToLrcTime(mPlayer.getCurrentPosition());
                    }
                    audioHandler.sendEmptyMessageDelayed(MSG_UPDATE_LRC_VIEW,TIME_UPDATE_LRC_VIEW);
                    break;
                case MSG_AUDIO_NOT_SUPPORT:
                    if(mAlertDialog == null) {
                        mPlayer.pause();
                        mAlertDialog = new AlertDialog.Builder(mContext).setTitle(R.string.audio_info_system_prompt)
                                .setMessage(R.string.audio_info_error_unsupport)
                                .setPositiveButton(R.string.btn_text_ok, new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        if (mOnAudioPlayerListener != null) {
                                            mOnAudioPlayerListener.OnAudioUnsupport();
                                        }
                                        mAlertDialog = null;
                                    }
                                }).setCancelable(false);
                        mAlertDialog.show();
                    }
                    break;
                default:
                    break;
            }
        }
    };
    public AudioPlayer(){
    }
    public void initAudioPlayer(Context context, String fileName, String dir, ReflectionImage reflectionImage, LongClickImageButton ibtnPauseStart,
                                LongClickImageButton ibtnPlayMode, SeekBar musicSeekBar, TextView curTime, TextView durTime, MarqueeTextView tvFileName, LrcView lrcView){
        if(mPlayer == null){
            mPlayer = new MediaPlayer();
        }
        mPlayer.setOnPreparedListener(this);
        mPlayer.setOnSeekCompleteListener(this);
        mPlayer.setOnCompletionListener(this);
        mPlayer.setOnInfoListener(this);
        mPlayer.setOnErrorListener(this);
        this.mContext = context;
        this.mCurrentFileName = fileName;
        this.mCurDir = dir;
        this.mAlbumReflectionImage = reflectionImage;
        this.ibtnPauseStart = ibtnPauseStart;
        this.ibtnPlayMode = ibtnPlayMode;
        this.mMusicSeekBar = musicSeekBar;
        this.mCurTime = curTime;
        this.mDurTime = durTime;
        this.mtvFileName = tvFileName;
        this.mLrcView = lrcView;
        this.mLocalMediaInfoList = new ArrayList<>();
        this.mLocalMediaInfoList.addAll(MediaFileManager.getInstance().getMediaFilesByType(dir, Constant.MediaType.AUDIO));
        mMusicSeekBar.setOnSeekBarChangeListener(this);
        dao = new MediaSettingDaoImpl(mContext);
        mCurPlayMode = dao.getMusicPlayMode();
        this.ibtnPlayMode.setImageResource(mCurPlayMode.getImgID());
        mCurrentFileIndex = -1;
        int index = 0;
        for(LocalMediaInfo m : mLocalMediaInfoList){
            if(m.getFileAllName().equals(mCurrentFileName)){
                mCurrentFileIndex = index;
                break;
            }
            index++;
        }
        if((mLocalMediaInfoList.size()) > 0 && (mCurrentFileIndex == -1)) {
            mCurrentFileIndex = 0;
            mCurrentFileName = mLocalMediaInfoList.get(mCurrentFileIndex).getFileAllName();
        }
        mAlbumReflectionImage.setImageResource(R.drawable.def_album_audio_image);
        mAsyncRetrieveAudio = new AsyncRetrieveAudio(mContext);
        mAsyncRetrieveAudio.setRetrieveAudioCompleteListener(this);
        new Thread(mAsyncRetrieveAudio).start();
        mtvFileName.setText(mLocalMediaInfoList.get(mCurrentFileIndex).getFileName());
        prepareAudio(mCurrentFileName);
    }

    public void deinitAudioPlayer(){
        audioHandler.removeMessages(MSG_UPDATE_ALBUM);
        audioHandler.removeMessages(MSG_UPDATE_AUDIO_INFO);
        audioHandler.removeMessages(MSG_UPDATE_PAUSE_VIEW);
        audioHandler.removeMessages(MSG_UPDATE_LRC_VIEW);
        audioHandler.removeMessages(MSG_AUDIO_NOT_SUPPORT);
        mAsyncRetrieveAudio.killSelf();
        GlobalDialog.destroyInstance();
        mPlayer.release();
    }

    @Override
    public void onCompleteAlbum(String name, Bitmap bm) {
        if (name.equals(mCurrentFileName)) {
            mCurBitmap = bm;
            audioHandler.sendEmptyMessage(MSG_UPDATE_ALBUM);
        }
    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        LogUtil.d(TAG,"onCompletion");
        if(mAudioStatus != AudioStatus.ERROR) {
            mMusicSeekBar.setEnabled(false);
            mAudioStatus = AudioStatus.STOPPED;
            switch (mCurPlayMode) {
                case REPEATOFF:
                    if ((mCurrentFileIndex == mLocalMediaInfoList.size() - 1) && (mOnAudioPlayerListener != null)) {
                        mOnAudioPlayerListener.OnAudioDestroy();
                    } else {
                        playNextAudio(false);
                    }
                    ;
                    break;
                case REPEATALL:
                    playNextAudio(true);
                    break;
                case PLAYONCE:
                    if (mOnAudioPlayerListener != null) {
                        mOnAudioPlayerListener.OnAudioDestroy();
                    }
                    break;
                case REPEATCURSONG:
                    prepareAudio(mCurrentFileName);
                    break;
                case SHUFFLE:
                    playRandomAudio();
                    break;
                default:
                    playNextAudio(true);
                    break;
            }
        }
    }

    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
        LogUtil.d(TAG,"onError");
        mAudioStatus = AudioStatus.ERROR;
        audioHandler.sendEmptyMessage(MSG_AUDIO_NOT_SUPPORT);
        return false;
    }

    @Override
    public boolean onInfo(final MediaPlayer mp, final int what, final int extra) {
        LogUtil.d(TAG,"Info what:" + what + "  extra:" + extra);
        return false;
    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        if(mAudioStatus == AudioStatus.PREPAREING){
            mAudioStatus = AudioStatus.INITED;
            mMusicSeekBar.setEnabled(true);
            int dur = mPlayer.getDuration();
            mMusicSeekBar.setMax(dur);
            mMusicSeekBar.setKeyProgressIncrement((dur > SEEKBAR_STEP_COUNT) ? (dur / SEEKBAR_STEP_COUNT) : 1);
            mtvFileName.setText(mLocalMediaInfoList.get(mCurrentFileIndex).getFileName());
            mAsyncRetrieveAudio.addTask(mCurrentFileName);
            String lrcFile;
            lrcFile = mCurrentFileName.substring(0,mCurrentFileName.lastIndexOf("."));
            lrcFile = lrcFile.concat(".lrc");
            audioHandler.removeMessages(MSG_UPDATE_LRC_VIEW);
            if(LrcUtil.isLrcFileExists(lrcFile)) {
                audioHandler.sendEmptyMessage(MSG_UPDATE_LRC_VIEW);
            }
            mLrcView.setLrcRowList(LrcUtil.parseLrcFile(lrcFile));
            mPlayer.start();
            mAudioStatus = AudioStatus.PLAYING;
            resetUpdateAudioInfo();
            audioHandler.sendEmptyMessage(MSG_UPDATE_PAUSE_VIEW);
            if(mPageAudioPlayerFileList != null){
                mPageAudioPlayerFileList.setCurAudioFileStatus(mCurrentFileIndex,false);
                mPageAudioPlayerFileList.setAudioFileSelection(mCurrentFileIndex);
            }
        }
    }

    @Override
    public void onSeekComplete(MediaPlayer mediaPlayer) {
        if(mPlayer.isPlaying()){
            mAudioStatus = AudioStatus.PLAYING;
        }else {
            if(mPlayer.getDuration() == mPlayer.getCurrentPosition()){
                mAudioStatus = AudioStatus.STOPPED;
            }else{
                mAudioStatus = AudioStatus.PAUSE;
            }
        }
        resetUpdateAudioInfo();
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        if(b) {
            if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
                mAudioStatus = AudioStatus.SEEKING;
                mPlayer.seekTo(i);
                audioHandler.removeMessages(MSG_UPDATE_AUDIO_INFO);
            }
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void OnAudioFileChange(String filePath,int pos) {
        if(filePath.equals(mLocalMediaInfoList.get(pos).getFileAllName())){
            mCurrentFileName = filePath;
            mCurrentFileIndex = pos;
            prepareAudio(mCurrentFileName);
        }
        GlobalDialog.getInstance(mContext).dismiss();
    }

    public void changeAudioPlayStatus(){
        if(mAudioStatus == AudioStatus.PLAYING){
            mPlayer.pause();
            mAudioStatus = AudioStatus.PAUSE;
            ibtnPauseStart.setImageResource(R.drawable.music_play);
        }else if (mAudioStatus == AudioStatus.PAUSE){
            mPlayer.start();
            mAudioStatus = AudioStatus.PLAYING;
            ibtnPauseStart.setImageResource(R.drawable.music_pause);
            resetUpdateAudioInfo();
        }
    }
    public void changeAudioPlayMode(){
        if((mAudioStatus.ordinal() > AudioStatus.INITED.ordinal())) {
            switch (mCurPlayMode) {
                case REPEATOFF:
                    mCurPlayMode = SettingUtil.MPLAYMODE.REPEATALL;
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_setting_play_mode_repeatall), Toast.LENGTH_SHORT).show();
                    break;
                case REPEATALL:
                    mCurPlayMode = SettingUtil.MPLAYMODE.PLAYONCE;
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_setting_play_mode_playonce), Toast.LENGTH_SHORT).show();
                    break;
                case PLAYONCE:
                    mCurPlayMode = SettingUtil.MPLAYMODE.REPEATCURSONG;
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_setting_play_mode_repeatcursong), Toast.LENGTH_SHORT).show();
                    break;
                case REPEATCURSONG:
                    mCurPlayMode = SettingUtil.MPLAYMODE.SHUFFLE;
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_setting_play_mode_shuffle), Toast.LENGTH_SHORT).show();
                    break;
                case SHUFFLE:
                    mCurPlayMode = SettingUtil.MPLAYMODE.REPEATOFF;
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_setting_play_mode_repeatoff), Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
            dao.setMusicPlayMode(mCurPlayMode);
            ibtnPlayMode.setImageResource(mCurPlayMode.getImgID());
        }
    }
    public void showAudioFileListSelect(){
        mPageAudioPlayerFileList = new PageAudioPlayerFileList(mContext,
                MediaFileManager.getInstance().getMediaFilesByType(mCurDir, Constant.MediaType.AUDIO));
        GlobalDialog.getInstance(mContext).setContentView(mPageAudioPlayerFileList);
        GlobalDialog.getInstance(mContext).setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialogInterface) {
                mPageAudioPlayerFileList = null;
            }
        });
        mPageAudioPlayerFileList.setCurAudioFileStatus(mCurrentFileIndex,mAudioStatus == AudioStatus.PAUSE);
        mPageAudioPlayerFileList.setAudioFileSelection(mCurrentFileIndex);
        mPageAudioPlayerFileList.setOnAudioPlayerChangeListener(this);
        GlobalDialog.getInstance(mContext).show();

    }
    public void playNextAudio(boolean isRepeat){
        if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
            int maxCount = mLocalMediaInfoList.size();
            int nextIndex = mCurrentFileIndex;
            nextIndex++;
            if (nextIndex == maxCount) {
                if(isRepeat){
                    nextIndex = 0;
                }else {
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_warn_last_file), Toast.LENGTH_SHORT).show();
                    return;
                }
            }
            mCurrentFileIndex =nextIndex;
            mCurrentFileName = mLocalMediaInfoList.get(mCurrentFileIndex).getFileAllName();
            prepareAudio(mCurrentFileName);

        }
    }
    public void playPreAudio(boolean isRepeat){
        if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
            int maxCount = mLocalMediaInfoList.size();
            int preIndex = mCurrentFileIndex;
            preIndex--;
            if (preIndex < 0) {
                if(isRepeat) {
                    preIndex = maxCount - 1;
                }else{
                    Toast.makeText(mContext, mContext.getResources().getString(R.string.music_warn_first_file), Toast.LENGTH_SHORT).show();
                    return;
                }
            }
            mCurrentFileIndex = preIndex;
            mCurrentFileName = mLocalMediaInfoList.get(mCurrentFileIndex).getFileAllName();
            prepareAudio(mCurrentFileName);
        }
    }
    public void playRandomAudio(){
        if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
            int randonIndex = new Random().nextInt(mLocalMediaInfoList.size());
            mCurrentFileIndex = randonIndex;
            mCurrentFileName = mLocalMediaInfoList.get(mCurrentFileIndex).getFileAllName();
            prepareAudio(mCurrentFileName);
        }
    }

    public void nextAudio(){
        switch (mCurPlayMode){
            case SHUFFLE:
                playRandomAudio();
                break;
            case REPEATALL:
                playNextAudio(true);
                break;
            default:
                playNextAudio(false);
                break;
        }
    }
    public void preAudio(){
        switch (mCurPlayMode){
            case SHUFFLE:
                playRandomAudio();
                break;
            case REPEATALL:
                playPreAudio(true);
                break;
            default:
                playPreAudio(false);
                break;
        }
    }
    public void forwardAudio(){
        if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
            mAudioStatus = AudioStatus.SEEKING;
            int dur = mPlayer.getDuration();
            int cur = mPlayer.getCurrentPosition();
            int newcur = cur + (((dur/MIN_BACKWARD_FORWARD_TIME) > SEEKBAR_STEP_COUNT) ? (dur / SEEKBAR_STEP_COUNT) : MIN_BACKWARD_FORWARD_TIME);
            if (newcur > dur) {
                newcur = dur;
            }
            mPlayer.seekTo(newcur);
        }
    }
    public void backwardAudio(){
        if(mAudioStatus.ordinal() > AudioStatus.INITED.ordinal()) {
            mAudioStatus = AudioStatus.SEEKING;
            int dur = mPlayer.getDuration();
            int cur = mPlayer.getCurrentPosition();
            int newcur = cur - ((dur > SEEKBAR_STEP_COUNT) ? (dur / SEEKBAR_STEP_COUNT) : 1);
            if (newcur < 0) {
                newcur = 0;
            }
            mPlayer.seekTo(newcur);
        }
    }
    private void prepareAudio(String file){
        mAudioStatus = AudioStatus.PREPAREING;
        try {
            mPlayer.reset();
            mPlayer.setDataSource(file);
            mtvFileName.setText(mLocalMediaInfoList.get(mCurrentFileIndex).getFileName());
            mAlbumReflectionImage.setImageResource(R.drawable.def_album_audio_image);
            mLrcView.setLrcRowList(null);
            mCurTime.setText(R.string.str_music_defaut_time);
            mDurTime.setText(R.string.str_music_defaut_time);
            mMusicSeekBar.setProgress(0);
            mMusicSeekBar.setMax(1);
            mPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void resetUpdateAudioInfo(){
        audioHandler.removeMessages(MSG_UPDATE_AUDIO_INFO);
        audioHandler.sendEmptyMessage(MSG_UPDATE_AUDIO_INFO);
    }
}
